#include "lib.h"

#include "ThreeCardRummyGame.h"
#include "tcpconn.h"
#include "shue.h"
#include "ThreeCardRummyHand.h"
#include "ThreeCardRummySQL.h"
#include "cardmgr.h"
#include "tcpranks.h"
#include "tcpsaved.h"

#define THREECARDRUMMY_NUMDECKS	1   // num decks in shue

typedef uint8 Outcomes;

static CardMgr stdMgr;

// the pay multipliers for when the player wins over the dealer
const static int32 anRaisePayTable[1][3] =
{
    { 1, 2, 4 }
};

// the pay multipliers for when the player wins the bonus bet.
const static int32 anSideBetPayTable[1][5] =
{
    { 4, 1, 2, 25, 100 }
};



/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::ThreeCardRummyGame(ThreeCardConnection* pConn, int32 nAnteBet, int32 nSideBet)
///
/// Constructor used when starting a brand new game
///
/// @param  pConn           (in) The object that provides the connection back to the client.
/// @param  nAnteBet        (in) The amount of initial (ante) bet that the player made
/// @param  nSideBet        (in) The amount of the player's bet on the progressive.
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
ThreeCardRummyGame::ThreeCardRummyGame(ThreeCardConnection* pConn, int32 nAnteBet, int32 nSideBet)
{
	m_pConn = pConn;
	m_nAnteBet = nAnteBet;
    m_nSideBet = nSideBet;

	m_pDealer = new ThreeCardRummyHand(&stdMgr);
	m_pPlayer = new ThreeCardRummyHand(&stdMgr);

    m_nRaisePayTable = m_pConn->getAnteBonusPayTable();
    m_nSideBetPayTable = m_pConn->getSideBetPayTable();

	m_bGameOK = true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn   ThreeCardRummyGame::ThreeCardRummyGame(ThreeCardConnection* pConn, ThreeCardSavedGame* saved)
///
/// This constructor is used when restoring an existing unfinished hand of Rummy
///
/// @param  pConn   (in) connects us back to the client
/// @param  pSaved  (in) the state information that we'll be restoring.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
ThreeCardRummyGame::ThreeCardRummyGame(ThreeCardConnection* pConn, ThreeCardSavedGame* saved)
{
	m_pConn = pConn;

	m_nAnteBet = 0;
	m_nSideBet = 0;
	m_pDealer = m_pPlayer = (ThreeCardRummyHand*) 0;
    m_nRaisePayTable = 0;
    m_nSideBetPayTable = 0;

	m_bGameOK = restore(saved);
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::~ThreeCardRummyGame()
///
/// Destructor
///
///////////////////////////////////////////////////////////////////////////////////////////////////
ThreeCardRummyGame::~ThreeCardRummyGame()
{
	delete m_pDealer;
	delete m_pPlayer;
}






///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8 ThreeCardRummyGame::deal(int32 balance)
///
/// Called when the user wants us to deal the cards for a new hand of Rummy.  This function 
/// creates the player and dealer hands, records all of the relevant information in the database,
/// and sends part of the deal results back to the client.
///
/// @param  nBalance    (in)  the player's current balance
///
/// @return  bool8  true if the deal completes successfully, false otherwise.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::deal(int32 balance)
{
	Shue* shue;
	bool8 result = false;

	ShueGameInfo sgi;
	sgi.gameId = TRICARD_RUMMY;
	sgi.machId = 0;

     #ifdef FORCEDSHOE 
       sgi.pid=m_pConn->GetPID();
    #endif


	shue = new Shue(&stdMgr, THREECARDRUMMY_NUMDECKS, sgi);

	if (shue && m_pDealer && m_pPlayer)
	{
        // pull the cards out of the shue and assign them in the correct order.
		shue->shuffle();

        // kharmon.  10-16-06.  #8226.  Save the deck seed, not the cards.
        ShueSave save;
        shue->save(&save);
        m_nDeckSeed = save.seed;

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		delete shue;

        // alert the database as to what has happened and let it run it's checks
		if (m_pConn->getDBInterface()->trackDeal(this))
		{
            m_pPlayer->rankHand();  // find player hand rank and point score.

            // Send the part of the deal that the user needs to see at this stage of the game.
			m_pConn->msgStart();
			m_pConn->msgAdd((uint8)'D');
			m_pConn->msgAdd((uint8)(m_pPlayer->getCount() & 0xff));
			m_pConn->msgReleaseBuf(m_pPlayer->out(m_pConn->msgGetBuf()));
		    m_pConn->msgReleaseBuf(m_pPlayer->outRank(m_pConn->msgGetBuf()));
		    m_pConn->msgReleaseBuf(m_pPlayer->outScore(m_pConn->msgGetBuf()));
			m_pConn->msgAdd(balance);
			m_pConn->msgEnd();

			result = true;
		}
	}

	return result;
}




///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     bool8   ThreeCardRummyGame::raise()
///
/// Called when the user raises.  This method figures out the outcome and payout info, updates 
/// the database with final game state, and then sends the final results back to the client.
///
///
/// @return bool8   true on success, false otherwise
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::raise()
{
    // the buck stops there.
	return finish('R');
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::fold()
///
/// Called when the user folds.  This method figures out the outcome and payout info, updates 
/// the database with final game state, and then sends the final results back to the client.
///
///
/// @return bool8   true on success, false otherwise
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::fold()
{
    // the buck stops there.
	return finish('F');
}



///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::finish(char raised)
///
/// This method does all of the real work when the user folds or calls.  This method figures 
/// out the outcome and payout info, updates the database with the final game state, and then 
/// sends the final results back to the client.
///
/// @param  raised     'R' if the player raised, 'F' if the user folded
///
/// @return bool8       true on success, false otherwise
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::finish(char raised)
{
	int32 payouts[3];   // 0th element = Ante Payout, 1st = Raise Payout, 2nd = Bonus Bet Payout.
    int32 balAdjust;    // the total amount to adjust the user's balance by
	int32 outcome;      // the final outcome: win, lose, push, etc.
	int32 balance;

    // okay, call calcPayouts to figure out what the final results are
	if (!calcPayouts(payouts, raised, &outcome, &balAdjust))
    {
        // hmmmm...something went belly up...not good.
		return false;
    }

    // we need to downcast so that we can call "setScores", which is not part of the
    // basic TDBInterface.
    ThreeCardRummySQL *pSQL = (ThreeCardRummySQL *) m_pConn->getDBInterface();

    // set the final player and dealer point scores.    
    pSQL->setScores(m_pPlayer->getScore(), m_pDealer->getScore());

    // run the results that calcPayout came up with though the database for final checks
    // and storage.
	if (pSQL->trackGameDone(raised, payouts[0], payouts[1], payouts[2],
		                    outcome, balAdjust, &balance, this))        // kharmon.  10-17-06.  #8226.  Extra parameter so that this call can save the cards from the hand.
	{
        // the database has been alerted and didn't reject the results, so let the client know 
        // what's going on.

		m_pConn->msgStart();
		m_pConn->msgAdd((uint8)'F');
		m_pConn->msgAdd((uint8)(m_pDealer->getCount() & 0xff));
		m_pConn->msgReleaseBuf(m_pDealer->out(m_pConn->msgGetBuf()));
		m_pConn->msgAdd(payouts[0]);
		m_pConn->msgAdd(payouts[1]);
		m_pConn->msgAdd(payouts[2]);
		m_pConn->msgReleaseBuf(m_pDealer->outRank(m_pConn->msgGetBuf()));
		m_pConn->msgReleaseBuf(m_pDealer->outScore(m_pConn->msgGetBuf()));
		m_pConn->msgReleaseBuf(m_pPlayer->outRank(m_pConn->msgGetBuf()));
		m_pConn->msgReleaseBuf(m_pPlayer->outScore(m_pConn->msgGetBuf()));
		m_pConn->msgAdd((uint8)(outcome & 0xff));
		m_pConn->msgAdd(balance);
		m_pConn->msgEnd();

		m_pConn->gameDone();	// delete "this"

		return true;
	}

	return false;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::calcPayouts(int32* payouts, char raised, int32* outcome, int32* winnings)
///
/// Does the work of figuring out who won the hand and how that affects the money.
///
/// @param  payouts           (out) array of 3 elements...0th element = ante payout, 1st = raise payout, 2cnd = bonus bet payout
/// @param  raised            (in)  did the user raise or fold?
/// @param  outcome           (out) Exactly who won, if anyone, and why they won.
/// @param  winnings          (out) how much the user's balance needs to change.
///
/// @return bool8   true if nothing goes wrong, false otherwise.
///
//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::calcPayouts(int32* payouts, char raised, int32* outcome, int32* winnings)
{    
    // init outputs:
    payouts[0] = 0;
    payouts[1] = 0;
    payouts[2] = 0;
	*winnings  = 0;
    *outcome = 0;

	m_pDealer->rankHand();  // find dealer hand rank and point score.

	m_pPlayer->rankHand();  // find dealer hand rank and point score.

    int32 nPlayerRank = m_pPlayer->getRank();

    int32 nDealerScore = m_pDealer->getScore();
    int32 nPlayerScore = m_pPlayer->getScore();


    if (m_nSideBet>0 && 'R'==raised)
    {
        // the player placed a side bet and raised...did they win anything?
        if (nPlayerScore < 13)
        {
            // yep, won something on sidebet
            *outcome = ((*outcome) & 0xfc) | O_PLAYER_WON_SIDEBET;
        }
        else
        {
            // nope, won nothing on sidebet
            *outcome = ((*outcome) & 0xfc) | O_PLAYER_LOST_SIDEBET;
        }
    }

    if (m_nAnteBet > 0)
    {
       	if (nDealerScore < nPlayerScore)    
        {
            // dealer wins: -ante, -raise
   	    	*outcome = ((*outcome) & 0x03) | O_DEALER_WON_ANTE;
        }
        else if (nDealerScore > nPlayerScore)
        {
            // player wins:
    	    *outcome = ((*outcome) & 0x03) | O_PLAYER_WON_ANTE;
        }
	    else
        {
            // tie.
		    *outcome = ((*outcome) & 0x03) | O_PUSH;
	    }


        if ('R' == raised)
	    {
		    if (!m_pDealer->qualifies())
            {
                // even if the player won, the player only gets 2 x Ante and 
                // sidebet money, also the player's raise is returned but not 
                // matched or multiplied.
       		    *outcome = ((*outcome) & 0x03) | O_DEALER_NOQUALIFY;
            }
	    }
        else if ('F' == raised)
        {
            // regardless of scores or sidebet outcome, the player 
            // lost because he folded.
    	    *outcome = ((*outcome) & 0x03) | O_PLAYER_FOLD;
        }
    }

    // SIDEBET
    if (((*outcome) & O_PLAYER_WON_SIDEBET) && !((*outcome) & O_PLAYER_FOLD))
    {
        // get the index for the multipler that we should use for calculating
        // the bonus bet payout.  The multiplier depends on the player's 
        // hand's point score.and rank
        int nMultiplier = 1;

        if(nPlayerRank == TCR_ACE_3CARD_STRAIGHT)
        {
            // special treatment for this "ace low" straight flush.
            nMultiplier = 4;
        }
        else if(nPlayerScore==0)
        {
            nMultiplier = 3;
        }
        else if(nPlayerScore<7)
        {
            nMultiplier = 2;
        }
        else if(nPlayerScore<11)
        {
            nMultiplier = 1;
        }
        else
        {
            nMultiplier = 0;
        }
        
        payouts[2] = m_nSideBet * anSideBetPayTable[m_nSideBetPayTable][nMultiplier];
        *winnings += m_nSideBet + payouts[2];
    }
    else if (((*outcome) & O_PLAYER_LOST_SIDEBET) && !((*outcome) & O_PLAYER_FOLD))
    {
        // player lost sidebet.
        payouts[2] = -m_nSideBet;
    }    

    // ANTE and RAISE
    if ((*outcome) & O_DEALER_WON_ANTE)
    {
        // player loses ante and raise bet
        payouts[0] = -m_nAnteBet;
        payouts[1] = -m_nAnteBet;
    }
    else if ((*outcome) & O_PLAYER_WON_ANTE)
    {
        // player gets ante, plus raise x multiplier
        payouts[0] = m_nAnteBet;   

        // raise multiplier based on player's hand score.
        int nMultiplier = 0;

        if(nPlayerScore==0)
        {
            nMultiplier = 2;
        }
        else if(nPlayerScore<6)
        {
            nMultiplier = 1;
        }
        
        payouts[1] = m_nAnteBet * anRaisePayTable[m_nRaisePayTable][nMultiplier];
        *winnings += m_nAnteBet*3 + payouts[1];   // player wins double ante, their raise bet, and the multiplied raise
    }
    else if ((*outcome) & O_PUSH)
    {
        payouts[0] = 0;
        payouts[1] = 0;
        *winnings += m_nAnteBet * 2;   // return ante and raise.
    }
    else if ((*outcome) & O_DEALER_NOQUALIFY)
    {
        payouts[0] = m_nAnteBet;
        payouts[1] = 0;
        *winnings += m_nAnteBet * 3;       // player wins double ante, and we return their raise.
    }
    else if ((*outcome) & O_PLAYER_FOLD)
    {
        payouts[0] = -m_nAnteBet;
        payouts[1] = 0;
        payouts[2] = -m_nSideBet;
        *winnings = 0;                  // player loses everything that they bet.
    }

	return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::save(ThreeCardSavedGame* saved)
///
/// Theoretically called to tell us to load this game's state into the CSavedGame object.
/// In practice, does nothing.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void
ThreeCardRummyGame::save(ThreeCardSavedGame* saved)
{
	// everything is already stored in the database (no need to store the shue)
	//
}





///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::restore(ThreeCardSavedGame* saved)
///
/// Does the actual work of loading the saved state back into this active game object.
///
/// @param  pSaved  (in) the state information that we'll be restoring.
///
/// @return bool8 always returns true.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
bool8
ThreeCardRummyGame::restore(ThreeCardSavedGame* saved)
{
	m_nAnteBet = saved->anteBet;
	m_nSideBet = saved->sideBet;
    m_nRaisePayTable = saved->anteBonusPayTable;
    m_nSideBetPayTable = saved->sideBetPayTable;

	m_pDealer = new ThreeCardRummyHand(&stdMgr);
	m_pPlayer = new ThreeCardRummyHand(&stdMgr);

    if(saved->bHasDeckSeed==true)
    {
        // kharmon.  10-17-06.  #8226.  This is a newer saved game that has a deckseed.
        // So, use that deckseed to regenerate the deck and redeal the cards for this hand.
	    ShueGameInfo sgi;
	    sgi.gameId = TRICARD_RUMMY;
	    sgi.machId = 0;

    	Shue *shue = new Shue(&stdMgr, THREECARDRUMMY_NUMDECKS, sgi);

        ShueSave save;
        save.seed = saved->nDeckSeed;
        save.next = 0;
        shue->restore(&save);

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		m_pPlayer->add(shue->getCard());
		m_pDealer->add(shue->getCard());

		delete shue;
    }
    else
    {
        // kharmon.  10-17-06.  #8226.  This is an older saved game that
        // has the cards stored in the save game record but no deckseed.  Just restore the cards.
        m_pDealer->restore(saved->dealerCards, saved->numDealerCards);
        m_pPlayer->restore(saved->playerCards, saved->numPlayerCards);
    }



	return m_pDealer->rankHand() && m_pPlayer->rankHand();
}



///////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     ThreeCardRummyGame::send()
///
/// This method is called to tell us to send the "restored" game state to the client when picking 
/// up on an existing game that was started but not completed in a previous session.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void
ThreeCardRummyGame::send()
{
	//
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
	//
	m_pConn->msgAdd(m_nAnteBet);
	m_pConn->msgAdd(m_nSideBet);
	m_pConn->msgAdd(m_nRaisePayTable);
	m_pConn->msgAdd(m_nSideBetPayTable);
	m_pConn->msgAdd((uint8)(m_pPlayer->getCount() & 0xff));
	m_pConn->msgReleaseBuf(m_pPlayer->out(m_pConn->msgGetBuf()));
	m_pConn->msgReleaseBuf(m_pPlayer->outRank(m_pConn->msgGetBuf()));
	m_pConn->msgReleaseBuf(m_pPlayer->outScore(m_pConn->msgGetBuf()));

}
